package belf.migrate.evaluate.helper;

import belf.migrate.api.exception.CatalogException;
import belf.migrate.api.job.Job;
import belf.migrate.api.table.schema.AbstractJdbcCatalog;
import belf.migrate.api.table.schema.IndexInfo;
import belf.migrate.api.taskconf.JobContext;
import belf.migrate.api.type.DbObjectType;
import belf.migrate.api.type.LogLevelType;
import belf.migrate.api.type.MatchingType;
import belf.migrate.api.util.DatabaseType;
import belf.migrate.api.util.PostLog;
import belf.migrate.evaluate.model.EvaluateBaseModel;
import belf.migrate.evaluate.model.EvaluateIndexModel;
import belf.migrate.evaluate.model.EvaluateIndexResult;
import belf.migrate.evaluate.model.EvaluateSinkResult;
import belf.migrate.evaluate.type.IndexType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.util.*;

import static belf.migrate.api.job.JobCode.EXECUTING;


@Slf4j
public class EvaluateIndexTool extends EvaluateBaseTool {

    private AbstractJdbcCatalog catalog;

    public EvaluateIndexTool(JobContext jobContext, Job job) {
        super(jobContext, job);
        this.catalog = jobContext.getSourceCatalog();
    }

    @Override
    public EvaluateIndexResult run() {

        String taskType = job.getTask().getTaskConf().getTaskType();
        String[] catalogs = taskType.split("-");
        DatabaseType sourceDatabaseType = DatabaseType.getDatabaseType(job.getTask().getTaskConf().getSourceConf().getCatalogName());
        if (!(sourceDatabaseType != null && sourceDatabaseType.getObjectTypes().contains(DbObjectType.INDEX))) {
            log.info("当前源端数据库不支持索引评估!");
            return null;
        }
        EvaluateIndexResult result = new EvaluateIndexResult();

        PostLog.sendLogModel(
                job.getJobId(),
                job.getTask().getTaskType().getName(),
                "开始进行索引评估!",
                LogLevelType.INFO.getType(),
                EXECUTING,
                jobContext.getProgress());
        log.info("开始进行索引评估!");

        Map<String, List<EvaluateBaseModel>> indexListMap = new HashMap<>();
        Map<String, EvaluateSinkResult> sinkResultMap = new HashMap<>();
        String databaseName = job.getTask().getTaskConf().getSourceConf().getDatabaseName();
        String schemaName = job.getTask().getTaskConf().getSourceConf().getSchemaName();
        String sourceCatalogName = null;
        String sinkCatalogName = null;
        Map<String, Map<String, Integer>> indexRiskMap = new HashMap<>();
        if (taskType.contains("-")) {
            sourceCatalogName = taskType.split("-")[0];
        }
        try {
            List<IndexInfo> indexInfos = catalog.listIndexInfos(databaseName, schemaName);
            if (CollectionUtils.isEmpty(indexInfos)) {
                log.error("索引评估：需要评估的索引为空");
                return null;
            }
            result.setAllObjectSize(indexInfos.size());
            for (int index = 0; index < indexInfos.size(); index++) {
                jobContext.addProgress(DbObjectType.INDEX.getTypeName(), indexInfos.size());
                IndexInfo indexInfo = indexInfos.get(index);
                long startTime = System.currentTimeMillis();
                MatchingType matchingType = MatchingType.FULL_MATCH;

                for (int i=0;i<job.getTask().getTaskConf().getEvaluateConf().getSinkDbs().size();i++) {
                    sinkCatalogName = job.getTask().getTaskConf().getEvaluateConf().getSinkDbs().get(i);
                    DatabaseType sinkDatabaseType = DatabaseType.getDatabaseType(sinkCatalogName);
                    if (!(sinkDatabaseType != null && sinkDatabaseType.getObjectTypes().contains(DbObjectType.INDEX))) {
                        result.getNotSupportCatalogs().add(sinkCatalogName);
                        continue;
                    }
                    EvaluateSinkResult sinkResult = getSinkResultList(sinkResultMap, sinkCatalogName);
                    List<EvaluateBaseModel> indexModelList = getBaseModelByMap(indexListMap, sinkCatalogName);
                    EvaluateBaseModel baseModel = new EvaluateBaseModel();
                    baseModel.setDatabaseName(databaseName);
                    baseModel.setSchemaName(schemaName);
                    baseModel.setName(indexInfos.get(index).getIndexName());

                    IndexType indexType = IndexType.getIndexType(sourceCatalogName, sinkCatalogName, indexInfo.getIndexType());
                    if (indexType != null) {
                        switch (indexType.getLevel()) {
                            case ERROR:
                                if (i == 0) {
                                    addMap(indexRiskMap, sinkCatalogName, indexType.getFrom().toUpperCase(Locale.ROOT));
                                }
                                matchingType = MatchingType.NO_MATCH;
                                break;
                            case WARNING:
                                matchingType = MatchingType.CONVERT_MATCH;
                                break;
                            case NORMAL:
                                break;
                        }
                        baseModel.setIndexDiff(new EvaluateIndexModel(indexInfo.getIndexName(), indexInfo.getIndexType(), indexType.getLevel().getName(), indexType.getDesc()));
                    }

                    baseModel.setMatchType(matchingType.getType());
                    //计算耗时
                    long endTime = System.currentTimeMillis();
                    String timeConsuming = String.valueOf((double) (endTime - startTime) / 1000);
                    baseModel.setTimeConsuming(String.format("%s秒", timeConsuming));
                    indexModelList.add(baseModel);
                    indexListMap.put(sinkCatalogName, indexModelList);
                    switch (matchingType.getId()) {
                        case 0:
                            sinkResult.addCompatibleSize(indexInfos.size());
                            break;
                        case 1:
                            sinkResult.addConvertSize(indexInfos.size());
                            break;
                        case 2:
                            sinkResult.addUnCompatibleSize(indexInfos.size());
                            break;
                        default:
                            sinkResult.addUnKnownSize(indexInfos.size());
                    }
                    sinkResultMap.put(sinkCatalogName, sinkResult);
                }

                String curLogMsg = String.format("索引 %s 评估已完成，目前进度：%s / %s.", indexInfos.get(index).getIndexName(), index + 1, indexInfos.size());
                PostLog.sendLogModel(
                        job.getJobId(),
                        job.getTask().getTaskType().getName(),
                        curLogMsg,
                        LogLevelType.INFO.getType(),
                        EXECUTING,
                        jobContext.getProgress());
                log.info(curLogMsg);
            }
            result.setObjectList(indexListMap);
            result.setSinkStatisticsMap(sinkResultMap);

            result.setIndexRisk(indexRiskMap);
        } catch (CatalogException e) {
            log.error("索引评估失败", e);
            PostLog.sendLogModel(
                    job.getJobId(),
                    job.getTask().getTaskType().getName(),
                    String.format("索引评估失败: %s", e.getMessage()),
                    LogLevelType.ERROR.getType(),
                    EXECUTING,
                    jobContext.getProgress());
        }
        log.info("索引评估成功完成!");
        PostLog.sendLogModel(
                job.getJobId(),
                job.getTask().getTaskType().getName(),
                "索引评估成功完成!",
                LogLevelType.INFO.getType(),
                EXECUTING,
                jobContext.getProgress());
        return result;
    }
}
